vue 的 slot
可以讓元件更靈活。
以便編譯順序來說,會先編譯父元件,然後才是子元件
// src/compiler/parser/index.js
function processSlot (el) {
if (el.tag === 'slot') {
el.slotName = getBindingAttr(el, 'name')
if (process.env.NODE_ENV !== 'production' && el.key) {
warn(
`\`key\` does not work on <slot> because slots are abstract outlets ` +
`and can possibly expand into multiple elements. ` +
`Use the key on a wrapping element instead.`
)
}
} else {
let slotScope
if (el.tag === 'template') {
slotScope = getAndRemoveAttr(el, 'scope')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && slotScope) {
warn(
`the "scope" attribute for scoped slots have been deprecated and ` +
`replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` +
`can also be used on plain elements in addition to <template> to ` +
`denote scoped slots.`,
true
)
}
el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
} else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && el.attrsMap['v-for']) {
warn(
`Ambiguous combined usage of slot-scope and v-for on <${el.tag}> ` +
`(v-for takes higher priority). Use a wrapper <template> for the ` +
`scoped slot to make it clearer.`,
true
)
}
el.slotScope = slotScope
}
const slotTarget = getBindingAttr(el, 'slot')
if (slotTarget) {
el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
// preserve slot as an attribute for native shadow DOM compat
// only for non-scoped slots.
if (el.tag !== 'template' && !el.slotScope) {
addAttr(el, 'slot', slotTarget)
}
}
}
}
當解析到 slot
屬性,會加上 slotTarget
属性,在 genData
中處理 slotTarget
// src/compiler/codegen/index.js
if (el.slotTarget && !el.slotScope) {
data += `slot:${el.slotTarget},`
}
以例子來說
有一個自定義元件 app-layout
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot>默認內容</slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
在父元件使用
<div id="app">
<app-layout>
<h1 slot="header">{{title}}</h1>
<p>{{msg}}</p>
<p slot="footer">{{desc}}</p>
</app-layout>
</div>
import AppLayout from "@/components/app-layout.vue";
export default {
data() {
return {
title: '標題',
msg: '內容',
desc: '其他'
}
},
components: {
AppLayout
}
})
父元件最後生成的 render
程式碼如下
with(this){
return _c('div',
[_c(
'app-layout',
[_c(
'h1',
{ attrs:{"slot":"header"},
slot:"header"
},
[ _v( _s(title) ) ]
),
_c(
'p',
[ _v( _s(msg) ) ]
),
_c(
'p',
{ attrs:{"slot":"footer"},
slot:"footer"
},
[ _v( _s(desc) ) ]
)
])
],
1
)
}